-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local types = require 'sml.types'
local method_misc = require 'method_misc'
local common_def = require 'common_def'

-- c_ctrl_operations
---@class c_ctrl_operations
---@field pd_operations integer
---@field ld_operations integer
---@field ctrl_operations integer 

-- c_ctrl_info
---@class c_ctrl_info
---@field last_update_timestamp integer
---@field properties integer
---@field operations c_ctrl_operations
---@field ctrl_name string
---@field ctrl_sn string
---@field fw_version string
---@field nvdata_version string
---@field hardware_revision string
---@field ld_present_conut integer
---@field ld_degraded_count integer
---@field ld_offline_count integer
---@field pd_present_count integer
---@field pddisk_present_count integer
---@field pddisk_prefail_count integer
---@field pddisk_fail_count integer
---@field memory_size integer
---@field memory_ecc_count integer
---@field memory_ecc_bucket_size integer
---@field memory_ce_count integer
---@field memory_uce_count integer
---@field max_pd_raid0 integer
---@field max_pd_raid1 integer
---@field max_pd_raid5 integer
---@field max_pd_raid6 integer
---@field max_pd_raid10 integer
---@field max_pd_raid50 integer
---@field max_pd_raid60 integer
---@field max_lds_per_array integer
---@field max_lds integer
---@field allow_mix_ssd_hdd integer
---@field allow_ssd_mix integer
---@field raid0_supported integer
---@field raid1_supported integer
---@field raid5_supported integer
---@field raid6_supported integer
---@field raid10_supported integer
---@field raid50_supported integer
---@field raid60_supported integer
---@field raid1adm_supported integer
---@field raid10adm_supported integer
---@field raid1triple_supported integer
---@field raid10triple_supported integer
---@field ssc_raid0_unsupported integer
---@field ssc_raid1_supported integer
---@field ssc_raid5_supported integer
---@field min_pd_raid0 integer
---@field min_pd_raid1 integer
---@field min_pd_raid5 integer
---@field min_pd_raid6 integer
---@field min_pd_raid10 integer
---@field min_pd_raid50 integer
---@field min_pd_raid60 integer
---@field ctrl_temp integer
---@field mode integer
---@field spare_activation_mode integer
---@field pcie_link_width integer
---@field nobattery_write_cache integer
---@field read_cache_percent integer
---@field write_cache_policy integer
---@field ctrl_warnig_info_reported integer
---@field cache_pinned integer
---@field maint_pd_fail_history integer
---@field device_interface integer
---@field min_strip integer
---@field max_strip integer
---@field consis_check_enabled integer
---@field consis_check_period integer
---@field consis_check_rate integer
---@field consis_check_repair integer
---@field consis_check_status integer
---@field consis_check_totalvd integer
---@field consis_check_completedvd integer
---@field consis_check_delay integer
---@field work_mode string[]
local c_ctrl_info = {}
c_ctrl_info.__index = c_ctrl_info

function c_ctrl_info.new(info)
    return setmetatable(info, c_ctrl_info)
end

local function raid_supported(v)
    return (v ~= nil and v ~= 0) and 1 or 0
end

local default_operations = {ctrl_operations = 0, ld_operations = 0, pd_operations = 0}
local ops, ctrl_operations, ld_operations, pd_operations
local retval = {
    pd_operations = {
        o_support_temperature = 0,
        o_support_crypto_erase = 0
    },
    ld_operations = {
        o_support_read_policy = 0,
        o_support_write_policy = 0,
        o_support_io_policy = 0,
        o_support_access_policy = 0,
        o_support_disk_cache_policy = 0,
        o_default_write_policy = 0
    },
    ctrl_operations = {
        o_support_jbod = 0,

        o_support_raid0 = 0,
        o_support_raid1 = 0,
        o_support_raid5 = 0,
        o_support_raid6 = 0,
        o_support_raid10 = 0,
        o_support_raid50 = 0,
        o_support_raid60 = 0,
        o_support_raid1adm = 0,
        o_support_raid10adm = 0,
        o_support_raid1triple = 0,
        o_support_raid10triple = 0,

        o_support_jbod_state = 0,
        o_support_mode_set = 0,
        o_support_raid = 0,
        o_support_hba = 0,
        o_support_mixed = 0,
        o_support_epd = 0,
        o_configured_drive_wcp = 0,
        o_unconfigured_drive_wcp = 0,
        o_hba_drive_wcp = 0
    }
}

local function update_retval(pd_op, ld_op, ctrl_op, info)
    retval.pd_operations.o_support_temperature = pd_op.support_temperature
    retval.pd_operations.o_support_crypto_erase = pd_op.support_crypto_erase

    retval.ld_operations.o_support_read_policy = ld_op.support_read_policy
    retval.ld_operations.o_support_write_policy = ld_op.support_write_policy
    retval.ld_operations.o_support_io_policy = ld_op.support_io_policy
    retval.ld_operations.o_support_access_policy = ld_op.support_access_policy
    retval.ld_operations.o_support_disk_cache_policy = ld_op.support_disk_cache_policy
    retval.ld_operations.o_default_write_policy = ld_op.default_write_policy

    retval.ctrl_operations.o_support_jbod = ctrl_op.support_jbod

    retval.ctrl_operations.o_support_raid0 = raid_supported(info.raid0_supported)
    retval.ctrl_operations.o_support_raid1 = raid_supported(info.raid1_supported)
    retval.ctrl_operations.o_support_raid5 = raid_supported(info.raid5_supported)
    retval.ctrl_operations.o_support_raid6 = raid_supported(info.raid6_supported)
    retval.ctrl_operations.o_support_raid10 = raid_supported(info.raid10_supported)
    retval.ctrl_operations.o_support_raid50 = raid_supported(info.raid50_supported)
    retval.ctrl_operations.o_support_raid60 = raid_supported(info.raid60_supported)
    retval.ctrl_operations.o_support_raid1adm = raid_supported(info.raid1adm_supported)
    retval.ctrl_operations.o_support_raid10adm = raid_supported(info.raid10adm_supported)
    retval.ctrl_operations.o_support_raid1triple = raid_supported(info.raid1triple_supported)
    retval.ctrl_operations.o_support_raid10triple = raid_supported(info.raid10triple_supported)

    retval.ctrl_operations.o_support_jbod_state = ctrl_op.support_jbod_state
    retval.ctrl_operations.o_support_mode_set = ctrl_op.support_mode_set
    retval.ctrl_operations.o_support_raid = ctrl_op.support_raid
    retval.ctrl_operations.o_support_hba = ctrl_op.support_hba
    retval.ctrl_operations.o_support_mixed = ctrl_op.support_mixed
    retval.ctrl_operations.o_support_epd = ctrl_op.support_epd
    retval.ctrl_operations.o_configured_drive_wcp = ctrl_op.configured_drive_wcp
    retval.ctrl_operations.o_unconfigured_drive_wcp = ctrl_op.unconfigured_drive_wcp
    retval.ctrl_operations.o_hba_drive_wcp = ctrl_op.hba_drive_wcp
end

---@param info  c_ctrl_info
local function sml_get_ctrl_support_operations(info)
    ops = info.operations or default_operations
    ctrl_operations = types.c_ctrl_operations:unpack(string.pack('I4', ops.ctrl_operations))
    ld_operations = types.t_ld_operations:unpack(string.pack('I4', ops.ld_operations))
    pd_operations = types.t_pd_operations:unpack(string.pack('I4', ops.pd_operations))
    if not ld_operations or not pd_operations or not ctrl_operations then
        error('invalid ctrl info operations')
    end

    update_retval(pd_operations, ld_operations, ctrl_operations, info)

    return retval
end

---@param info  c_ctrl_info
local function sml_get_ctrl_properties(info, type_id)
    local p = types.t_ctrl_properties:unpack(string.pack('I4', info.properties or 0))
    local is_pmc_raid = method_misc:test_controller_vendor(type_id, common_def.VENDER_PMC)
    local raw_state = info:get_support_operations().ctrl_operations.o_support_jbod_state
    local job_enabl_value = (raw_state == 1) and p.jbod_enabled or common_def.INVALID_U8

    return {
        o_copyback_enabled = is_pmc_raid and common_def.INVALID_U8 or p.copyback_enabled,
        o_smarter_copyback_enabled = is_pmc_raid and common_def.INVALID_U8 or p.smarter_copyback_enabled,
        o_jbod_enabled =  is_pmc_raid and common_def.INVALID_U8 or job_enabl_value,
        o_support_crypto_erase = info:get_support_operations().pd_operations.support_crypto_erase,
        o_consis_check_enabled = info.consis_check_enabled or 0,
        o_consis_check_period = info.consis_check_period or 0,
        o_consis_check_rate = info.consis_check_rate or 0,
        o_consis_check_repair = info.consis_check_repair or 0,
        o_consis_check_delay = info.consis_check_delay or 0,
        o_consis_check_totalvd = info.consis_check_totalvd or 0,
        o_consis_check_completedvd = info.consis_check_completedvd or 0,
        o_consis_check_status = info.consis_check_status or 0
    }
end

function c_ctrl_info:get_ctrl_device_interface()
    return types.interface_type_map[self.device_interface] or 'unknown'
end

function c_ctrl_info:get_ctrl_strip_szie_options()
    local min = (not self.min_strip or self.min_strip == common_def.INVALID_U8 or self.min_strip == 0) and
        common_def.INVALID_U8 or ((2 ^ self.min_strip) * 512)
    local max = (not self.max_strip or self.max_strip == common_def.INVALID_U8 or self.max_strip == 0) and
        common_def.INVALID_U8 or ((2 ^ self.max_strip) * 512)
    return min, max
end

local operations, result, ctrl_types
function c_ctrl_info:get_raid_types()
    result = {}
    operations = self:get_support_operations()
    ctrl_types = string.unpack('I4', types.t_support_ctrl_operations:pack(
        operations.ctrl_operations))
    for i = 0, 10 do
        if (ctrl_types & (1 << i)) ~= 0 then
            result[#result + 1] = types.level_array[i + 1]
        end
    end
    return result
end

function c_ctrl_info:get_support_operations()
    if not self.__support_operations then
        self.__support_operations = sml_get_ctrl_support_operations(self)
    end
    return self.__support_operations
end

function c_ctrl_info:get_support_properties(type_id)
    if not self.__support_properties then
        self.__support_properties = sml_get_ctrl_properties(self, type_id)
    end
    return self.__support_properties
end

return c_ctrl_info
